package com.acme.cli;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import com.acme.bankapp.domine.client.*;
import com.acme.bankapp.domine.bank.*;
import com.acme.bankapp.domine.account.*;
import com.acme.bankapp.exceptions.ClientExistsException;
import com.acme.bankapp.exceptions.NoEnoughFundsException;
import com.acme.bankapp.exceptions.OverDraftLimitExceededException;
import com.acme.bankapp.service.bank.BankDataLoader;
import com.acme.bankapp.service.bank.BankService;
import com.acme.bankapp.service.bank.impl.BankServiceImpl;

/**
 * The following scenarios are available:
 * 1) "BankApplication -m" In-memory mode. Creates a few entities
 * programmatically, prints them.
 * 2) "BankApplication -s" Forces to save bank to bank.ser file (file name can
 * not be specified for simplicity)
 * 3) "BankApplication -l" Loads the bank from bank.ser file
 * 4) "BankApplication -lf" Loads feed file from bank.data file, tries to add
 * data to the bank and then saves it to bank.ser file
 */
public class BankApplication {

    private static final String ALREADY_LOADED =
            "Bank has already been loaded via another program argument. "
            + "Only one option from the list {-m, -lf, -l} "
            + "may be specified at a time.";
    private static final String BANK_IS_NOT_LOAD_MESSAGE = "Bank is not loaded";
    private static final String SER_FILE = "bank.ser";
    private static final String FEED_FILE = "bank.dat";

    private static BankDataLoader bankDataLoader;

    private static BankService bankService;

    public static void main(final String[] args) throws IOException  {
        Set<String> arguments = new HashSet<String>();
        for (String string : args) {
            arguments.add(string);
        }
        initServices();

        boolean bankLoaded = false;
        Bank bank = null;
        if (arguments.contains("-m")) {
            bank = createInMemoryBank();
            bankLoaded = true;
        }

        if (arguments.contains("-lf")) {
            if (bankLoaded) {
                System.out.println(ALREADY_LOADED);
                return;
            }
            bank = loadBankFromFeed();
            bankLoaded = true;
        }

        if (arguments.contains("-l")) {
            if (bankLoaded) {
                System.out.println(ALREADY_LOADED);
                return;
            }
            bank = loadBankFromDataSource();
            bankLoaded = true;
        }
        if (bank == null) {
            System.out.println(BANK_IS_NOT_LOAD_MESSAGE);
            return;
        }

        bankService.printBalance(bank);
        bankService.printMaximumAmountToWithdraw(bank);

        if (arguments.contains("-s")) {
            saveTheBankToDataSource(bank);
        }

    }


    private static void initServices() {
        bankService = new BankServiceImpl();
        bankDataLoader = new BankDataLoader();
        bankDataLoader.setBankService(bankService);

    }

    private static void saveTheBankToDataSource(final Bank bank)
            throws IOException {
        bankService.saveBank(bank, SER_FILE);
    }

    private static Bank loadBankFromDataSource() throws IOException {
        return bankService.loadBank(SER_FILE);
    }

    private static Bank loadBankFromFeed() throws IOException {
        Bank bank = new Bank();
        bankDataLoader.load(bank, FEED_FILE);
        saveTheBankToDataSource(bank);
        return bank;
    }

    private static Bank createInMemoryBank() {

        Bank bank = bankService.createNewBank();

        Client client1 = null;
        Client client2 = null;

        try {
            Account account1 = new SavingAccount(100);
            Account account2 = new CheckingAccount(100, 10);
            Account account3 = new SavingAccount(50);
            Account account4 = new CheckingAccount(75, 20);

            try {
                client1 = bankService.addClient(bank, "John", Gender.MALE);
                client2 = bankService.addClient(bank, "Miranda", Gender.FEMALE);
                client1.addAccount(account1);
                client1.addAccount(account2);
                client2.addAccount(account3);
                client2.addAccount(account4);
            } catch (ClientExistsException e) {
                System.out.println(e.getMessage());
            }

            account1.deposit(100);
            try {
                account3.withdraw(50);
                account4.withdraw(80);
            } catch (OverDraftLimitExceededException e) {
                System.out.println(e.getMessage());
            } catch (NoEnoughFundsException e) {
                System.out.println(e.getMessage());
            }

        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }

        return bank;
    }
}
